package com.atguigu.gmall.list.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.list.dao.GoodsDao;
import com.atguigu.gmall.list.service.ListService;
import com.atguigu.gmall.model.list.*;
import com.atguigu.gmall.model.product.BaseCategoryView;
import com.atguigu.gmall.model.product.BaseTrademark;
import com.atguigu.gmall.model.product.SkuAttrValue;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.product.client.ProductClientFeign;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: lx
 * @Time: 15:28
 * @Description:
 */
@Service
public class ListServiceImpl implements ListService {


    @Autowired
    private GoodsDao goodsDao;
    @Autowired
    private ProductClientFeign productClientFeign;

    //上架 保存库存信息到ES中  Java代码连接ES索引库？
    //DSL 语句 连接ES索引库
    @Override
    public void onSale(Long skuId) {
        Goods goods = new Goods();// iphone12 颜色 红色  版本
 /*       goods.setId(123L);
        goods.setTitle("大腕都好有钱");*/
        //1:SkuInfo
        SkuInfo skuInfo = productClientFeign.getSkuInfo(skuId);
        goods.setId(skuInfo.getId());
        goods.setTitle(skuInfo.getSkuName());
        goods.setCreateTime(new Date());
        goods.setPrice(skuInfo.getPrice().doubleValue());
        goods.setDefaultImg(skuInfo.getSkuDefaultImg());
        //2:分类
        BaseCategoryView categoryView = productClientFeign.
                getCategoryView(skuInfo.getCategory3Id());
        goods.setCategory1Id(categoryView.getCategory1Id());
        goods.setCategory2Id(categoryView.getCategory2Id());
        goods.setCategory3Id(categoryView.getCategory3Id());
        goods.setCategory1Name(categoryView.getCategory1Name());
        goods.setCategory2Name(categoryView.getCategory2Name());
        goods.setCategory3Name(categoryView.getCategory3Name());
        //3:品牌
        BaseTrademark trademark = productClientFeign.getTrademark(skuInfo.getTmId());
        goods.setTmId(trademark.getId());
        goods.setTmName(trademark.getTmName());
        goods.setTmLogoUrl(trademark.getLogoUrl());
        //4：平台属性及值
        List<SkuAttrValue> attrValueList =
                productClientFeign.getAttrValueList(skuInfo.getId());
        //////////////////
        List<SearchAttr> searchAttrList = attrValueList.stream().map((skuAttrValue) -> {
            SearchAttr searchAttr = new SearchAttr();
            //平台属性ID
            searchAttr.setAttrId(skuAttrValue.getBaseAttrInfo().getId());
            //平台属性名称
            searchAttr.setAttrName(skuAttrValue.getBaseAttrInfo().getAttrName());
            //平台属性值名称
            searchAttr.setAttrValue(skuAttrValue.getBaseAttrValue().getValueName());
            return searchAttr;
        }).collect(Collectors.toList());
        goods.setAttrs(searchAttrList);
        goodsDao.save(goods);
    }

    @Autowired
    private RedisTemplate redisTemplate;

    //更新商品热度
    @Override
    public void hotScore(Long skuId) {
        //1:更新Redis  五大数据类型   适合呢？  Hash类型  List  Set ZSet
        //有序集合 类型
        //  小明  语文 100  数学 99 英语 88
        //  小张  语文 100  数学 99 英语 88
        String hotScore = "hotScore";
        //原子性操作 自增
        Double score = redisTemplate.opsForZSet()
                .incrementScore(hotScore, skuId, 1);
        //2: 10 20 30 更新ES   88
        System.out.println("不更新ES热度分数：" + score);
        if (score % 10 == 0) {
            System.out.println("更新ES热度分数：" + score);
            Optional<Goods> byId = goodsDao.findById(skuId);//ID 如果相同 之前删除
            Goods goods = byId.get();
            goods.setHotScore(Math.round(score));
            goodsDao.save(goods);
        }
    }

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    //开始搜索
    @Override
    public SearchResponseVo list(SearchParam searchParam) {
        SearchRequest searchRequest = new SearchRequest("goods");
        //1:构建条件对象
        SearchSourceBuilder searchSourceBuilder = buildSourceBuider(searchParam);
        searchRequest.source(searchSourceBuilder);
        SearchResponseVo vo = null;
        try {
            //2:执行搜索
            SearchResponse searchResponse =
                    restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            //3:解析结果
            vo = buildSearchResponse(searchResponse);
            //总页数计算
            //总记录数 已经有了
            //当前页
            vo.setPageNo(searchParam.getPageNo());
            //每页数
            vo.setPageSize(searchParam.getPageSize());
            //总页数
            vo.setTotalPages((vo.getTotal() + vo.getPageSize() -1)/vo.getPageSize());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return vo;
    }

    //解析结果
    private SearchResponseVo buildSearchResponse(SearchResponse searchResponse) {
        SearchResponseVo  vo = new SearchResponseVo();

        //4:品牌     一个父亲 带着二个孩子    select  max(id) as iddd  from 表
        Map<String, Aggregation> aggregationMap = searchResponse.getAggregations().asMap();

        ParsedLongTerms tmIdAgg = (ParsedLongTerms) aggregationMap.get("tmIdAgg");
        //  private List<SearchResponseTmVo> trademarkList;
        List<SearchResponseTmVo> responseTmVoList = tmIdAgg.getBuckets().stream().map((bucket) -> {
            //品牌对象
            SearchResponseTmVo tmVo = new SearchResponseTmVo();
            //品牌ID
            tmVo.setTmId(Long.parseLong(bucket.getKeyAsString()));
            //品牌名称
            ParsedStringTerms tmNameAgg = ((Terms.Bucket) bucket).getAggregations().get("tmNameAgg");
            tmVo.setTmName(tmNameAgg.getBuckets().get(0).getKeyAsString());
            //品牌Logo
            ParsedStringTerms tmLogoUrlAgg = ((Terms.Bucket) bucket).getAggregations().get("tmLogoUrlAgg");
            tmVo.setTmLogoUrl(tmLogoUrlAgg.getBuckets().get(0).getKeyAsString());
            return tmVo;
        }).collect(Collectors.toList());
        vo.setTrademarkList(responseTmVoList);
        //5:平台
        ParsedNested attrsAgg = (ParsedNested) aggregationMap.get("attrsAgg");
        ParsedLongTerms attrIdAgg = attrsAgg.getAggregations().get("attrIdAgg");
        //    private List<SearchResponseAttrVo> attrsList = new ArrayList<>();
        List<SearchResponseAttrVo> attrsList = attrIdAgg.getBuckets().stream().map((bucket) -> {
            SearchResponseAttrVo attrVo = new SearchResponseAttrVo();
            //平台属性ID
            attrVo.setAttrId(Long.parseLong(((Terms.Bucket) bucket).getKeyAsString()));
            //平台属性名称
            ParsedStringTerms attrNameAgg = ((Terms.Bucket) bucket).getAggregations().get("attrNameAgg");
            attrVo.setAttrName(attrNameAgg.getBuckets().get(0).getKeyAsString());
            //平台属性值名称 集合
            ParsedStringTerms attrValueAgg = ((Terms.Bucket) bucket).getAggregations().get("attrValueAgg");
            attrVo.setAttrValueList(attrValueAgg.getBuckets().stream().map((b) -> {
                return ((Terms.Bucket) b).getKeyAsString();
            }).collect(Collectors.toList()));
            return attrVo;
        }).collect(Collectors.toList());
        vo.setAttrsList(attrsList);
        //解析结果
        SearchHits hits = searchResponse.getHits();
        //1:总记录数
        vo.setTotal(hits.getTotalHits());
        SearchHit[] hits1 = hits.getHits();
        if(null != hits1 && hits1.length > 0){
            //2：商品结果集
            List<Goods> goodsList = Arrays.stream(hits1).map((hit) -> {
                //普通搜索商品的对象  名称是普通名称
                Goods goods = JSONObject.parseObject(hit.getSourceAsString(), Goods.class);
                //3:高亮
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                if(highlightFields != null && highlightFields.size() >0){
                    HighlightField title = highlightFields.get("title");
                    if(null != title){
                        //有高亮名称 优先使用高亮名称   没有使用原来 的普通名称
                        goods.setTitle(title.fragments()[0].toString());
                    }
                }
                return goods;
            }).collect(Collectors.toList());
            vo.setGoodsList(goodsList);
        }
        return vo;
    }


    //构建条件对象
    private SearchSourceBuilder buildSourceBuider(SearchParam searchParam) {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //9个入参   6部分
        //组合条件对象     where     and  or not
        // must == and  should = or  mustnot = not
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //1:关键词              基础    必填项    默认值
        String keyword = searchParam.getKeyword();
        if (!StringUtils.isEmpty(keyword)) {
            //组合条件对象 组装必填值
            //matchQuery : 我是中国人
            // 第一步： 先分词  我  是 中国  国人 中国人
            // 第二步： like %我% AND like %是% AND like %中国% 。。。。。
            boolQueryBuilder.must(
                    QueryBuilders.matchQuery("title", keyword)
                            .operator(Operator.AND));
        } else {
            //随意处理
            boolQueryBuilder.must(QueryBuilders.matchAllQuery());//like 分词
        }
        //////////////////////
        //2:品牌   品牌ID:品牌的名称               过滤条件
        String trademark = searchParam.getTrademark();
        if (!StringUtils.isEmpty(trademark)) {
            //termQuery : 华为
            // 只能一步： tmName == 华为
            String[] t = StringUtils.split(trademark, ":");
            boolQueryBuilder.filter(QueryBuilders.termQuery("tmId", t[0]));
        }
        //3:三级分类           过滤条件
        Long category1Id = searchParam.getCategory1Id();
        if (null != category1Id) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category1Id", category1Id));
        }
        Long category2Id = searchParam.getCategory2Id();
        if (null != category2Id) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category2Id", category2Id));
        }
        Long category3Id = searchParam.getCategory3Id();
        if (null != category3Id) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category3Id", category3Id));
        }
        //4:平台属性数组       过滤条件
        String[] props = searchParam.getProps();
        if (null != props && props.length > 0) {
            //新的组合对象
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
            for (String prop : props) {
                // prop =  平台属性的ID:平台属性值的名称:平台属性的名称
                String[] p = prop.split(":");
                BoolQueryBuilder subBoolQuery = QueryBuilders.boolQuery();
                //平台属性ID
                subBoolQuery.must(QueryBuilders.termQuery("attrs.attrId", p[0]));
                subBoolQuery.must(QueryBuilders.termQuery("attrs.attrValue", p[1]));
                boolQuery.must(QueryBuilders.nestedQuery(
                        "attrs",subBoolQuery,ScoreMode.None));
            }
            //组合对象
            boolQueryBuilder.filter(boolQuery);
        }
        searchSourceBuilder.query(boolQueryBuilder);//注意：只能设置一次
        //////////////////
        //5:排序 （综合 1） 价格2) （新品3）  order=1:desc 或 1:asc
        String order = searchParam.getOrder();
        if(!StringUtils.isEmpty(order)){
            String[] o = StringUtils.split(order, ":");
            String s = "";
            switch (o[0]){
                case "1" : s = "hotScore";break;
                case "2" : s = "price";break;
                case "3" : s = "createTime";break;
            }
            searchSourceBuilder.sort(s,
                    o[1].equalsIgnoreCase("desc") ? SortOrder.DESC : SortOrder.ASC);
        }else{
            //默认排序  综合 （热度） 降序
            searchSourceBuilder.sort("hotScore", SortOrder.DESC);
        }

        //6:分页
        Integer pageNo = searchParam.getPageNo();
        Integer pageSize = searchParam.getPageSize();
        searchSourceBuilder.from((pageNo - 1) * pageSize);//开始行
        searchSourceBuilder.size(pageSize);//每页数
        //7:高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title").preTags("<font style='color:red'>")
                                        .postTags("</font>");
        searchSourceBuilder.highlighter(highlightBuilder);
    /*    HighlightBuilder highlighter = searchSourceBuilder.highlighter();
        highlighter.preTags("<font style='color:red'>");*/
        //8: 品牌进行聚合
        // sql  select 去重. tmName from 表  where ...
        // DSL   聚合  （2:华为 2:华为 3苹果   4小米）
        // 品牌ID为父聚合  品牌的名称为子聚合  品牌的图片为子聚合
        searchSourceBuilder.aggregation(  // select max(tmId) tmIdAgg from 表 
                AggregationBuilders.terms("tmIdAgg").field("tmId")
                        .subAggregation(AggregationBuilders.terms("tmNameAgg").field("tmName"))
                        .subAggregation(AggregationBuilders.terms("tmLogoUrlAgg").field("tmLogoUrl"))
        );
        //9:平台属性聚合  嵌套 类型
        searchSourceBuilder.aggregation(
                AggregationBuilders.nested("attrsAgg","attrs")
                    .subAggregation(AggregationBuilders.terms("attrIdAgg").field("attrs.attrId")
                    .subAggregation(AggregationBuilders.terms("attrNameAgg").field("attrs.attrName"))
                    .subAggregation(AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue"))));

        System.out.println(searchSourceBuilder.toString());
        return searchSourceBuilder;
    }


    //下架
    @Override
    public void cancelSale(Long skuId) {
        goodsDao.deleteById(skuId);//Java
    }
}
